package com.easylinkin.linkappapi.taskscheduler.service.impl;

import com.easylinkin.linkappapi.common.exceptions.BusinessException;
import com.easylinkin.linkappapi.taskscheduler.constant.JobEntityConstant.IsNowRunEnum;
import com.easylinkin.linkappapi.taskscheduler.constant.JobEntityConstant.JobStatusEnum;
import com.easylinkin.linkappapi.taskscheduler.entity.JobEntity;
import com.easylinkin.linkappapi.taskscheduler.service.JobEntityService;
import com.easylinkin.linkappapi.taskscheduler.service.TaskSchedulerService;
import java.util.Date;
import javax.annotation.Resource;
import org.quartz.CronScheduleBuilder;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.JobKey;
import org.quartz.ObjectAlreadyExistsException;
import org.quartz.Scheduler;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.TriggerKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

/**
 * <p>定时任务调度器</p>
 *
 * @author TongJie
 * @since 2020/6/5 15:21
 */

@Component
public class TaskSchedulerImpl implements TaskSchedulerService {

    private static final Logger LOGGER = LoggerFactory.getLogger(TaskSchedulerImpl.class);
    @Autowired
    private Scheduler scheduler;
    @Resource
    private JobEntityService jobEntityService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveTask(JobEntity jobEntity) {
        /*
         * 1.将任务记录插入 linkapp_job_entity
         * 2.将任务交由Scheduler安排触发
         * 3.如果是立即执行，则首先触发一次任务
         */
        try {
            jobEntity.setTriggerName("trigger_" + jobEntity.getJobName());
            jobEntity.setTriggerGroup("trigger_" + jobEntity.getJobGroup());
            jobEntityService.save(jobEntity);
        } catch (Exception e) {
            throw new BusinessException("定时任务插入数据库失败: "+ e.getMessage());
        }
        try {
            Class cls = Class.forName(jobEntity.getJobClassName());
            cls.newInstance();
            // 构建job信息
            JobDetail job = JobBuilder.newJob(cls).withIdentity(jobEntity.getJobName(), jobEntity.getJobGroup())
                .withDescription(jobEntity.getRemark()).build();
            // 触发时间点
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(jobEntity.getCronExpression());
            Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger_" + jobEntity.getJobName(), jobEntity.getJobGroup()).startNow()
                .withSchedule(cronScheduleBuilder).build();
            // 传递的参数
            job.getJobDataMap().put("jobtasktype", jobEntity.getJobTaskType());
            job.getJobDataMap().put("jobtype", jobEntity.getJobType());
            job.getJobDataMap().put("apiurl", jobEntity.getApiUrl());
            job.getJobDataMap().put("params", jobEntity.getParams());
            // 交由Scheduler安排触发
            if (JobStatusEnum.ON.getType().equals(jobEntity.getJobStatus())) {
                scheduler.scheduleJob(job, trigger);
            }
            // 如果是立即运行则首先触发一次任务
            if (JobStatusEnum.ON.getType().equals(jobEntity.getJobStatus()) && IsNowRunEnum.YES.getType().equals(jobEntity.getIsNowRun())) {
                JobKey key = new JobKey(jobEntity.getJobName(), jobEntity.getJobGroup());
                scheduler.triggerJob(key, job.getJobDataMap());
            }
        } catch (Exception e) {
            if (e instanceof ObjectAlreadyExistsException) {
                LOGGER.error(e.getMessage(), e);
                throw new BusinessException("任务名称存在重复，请换个任务名重试");
            }
            throw new BusinessException("新增任务失败,错误信息如下:"+ e.getMessage());
        }
    }


    /**
     * 校验jobEntity
     */
    private void checkJobEntity(JobEntity jobEntity) {
        Assert.notNull(jobEntity, "jobEntity 为空");
        Assert.hasLength(jobEntity.getJobName(), "jobName 为空");
        Assert.hasLength(jobEntity.getJobGroup(), "jobGroup 为空");
    }

    @Override
    public void trigger(JobEntity record) {
        try {
            checkJobEntity(record);
            Class cls = Class.forName(record.getJobClassName());
            cls.newInstance();
            // 构建job信息
            JobDetail job = JobBuilder.newJob(cls).withIdentity(record.getJobName(), record.getJobGroup())
                .withDescription(record.getRemark()).build();
            // 触发时间点
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(record.getCronExpression());
            Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger_" + record.getJobName(), record.getJobGroup()).startNow()
                .withSchedule(cronScheduleBuilder).build();
            // 传递的参数
            job.getJobDataMap().put("jobtasktype", record.getJobTaskType());
            job.getJobDataMap().put("jobtype", record.getJobType());
            job.getJobDataMap().put("apiurl", record.getApiUrl());
            job.getJobDataMap().put("params", record.getParams());
            scheduler.scheduleJob(job, trigger);
            JobKey key = new JobKey(record.getJobName(), record.getJobGroup());
            scheduler.triggerJob(key, job.getJobDataMap());
        } catch (Exception e) {
            throw new BusinessException("触发任务失败：" + e.getMessage());
        }
    }

    @Override
    public void pause(JobEntity jobEntity) {
        try {
            checkJobEntity(jobEntity);
            JobKey key = new JobKey(jobEntity.getJobName(), jobEntity.getJobGroup());
            JobDetail jobDetail = scheduler.getJobDetail(key);
            if (null != jobDetail) {
                LOGGER.info("暂停job：" + jobDetail.toString());
            }else {
                LOGGER.info("需要暂停的JobDetail为空");
            }
            scheduler.pauseJob(key);
            //如果暂停后恢复，会立刻执行一次，所以暂停时先删掉定时任务，恢复是新增
            TriggerKey triggerKey = TriggerKey.triggerKey(jobEntity.getJobName(), jobEntity.getJobGroup());
            // 停止触发器
            scheduler.pauseTrigger(triggerKey);
            // 移除触发器
            scheduler.unscheduleJob(triggerKey);
            // 删除任务
            scheduler.deleteJob(JobKey.jobKey(jobEntity.getJobName(), jobEntity.getJobGroup()));
            LOGGER.info("暂停任务：" + JobKey.jobKey(jobEntity.getJobName()));
        } catch (Exception e) {
            throw new BusinessException("暂停任务失败:" + e.getMessage());
        }
    }

    @Override
    public void resume(JobEntity jobEntity) {
        try {
            checkJobEntity(jobEntity);
            JobKey key = new JobKey(jobEntity.getJobName(), jobEntity.getJobGroup());
            JobDetail jobDetail = scheduler.getJobDetail(key);
            if (null != jobDetail) {
                scheduler.resumeJob(key);//重启job
                LOGGER.info("重启前JobDetail：" + scheduler.getJobDetail(key).toString());
            }else {
                LOGGER.info("重启前JobDetail为空");
                //新增任务
                Class cls = Class.forName(jobEntity.getJobClassName());
                cls.newInstance();
                // 构建job信息
                JobDetail job = JobBuilder.newJob(cls).withIdentity(jobEntity.getJobName(), jobEntity.getJobGroup())
                        .withDescription(jobEntity.getRemark()).build();
                // 触发时间点
                CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(jobEntity.getCronExpression());
                Trigger trigger = TriggerBuilder.newTrigger()
                        .withIdentity(jobEntity.getTriggerName(), jobEntity.getTriggerGroup()).startNow()
                        .withSchedule(cronScheduleBuilder).build();
                // 传递的参数
                job.getJobDataMap().put("jobtasktype", jobEntity.getJobTaskType());
                job.getJobDataMap().put("jobtype", jobEntity.getJobType());
                job.getJobDataMap().put("apiurl", jobEntity.getApiUrl());
                job.getJobDataMap().put("params", jobEntity.getParams());
                // 交由Scheduler安排触发
                scheduler.scheduleJob(job, trigger);
            }
            JobDetail jobDetail2 = scheduler.getJobDetail(key);
            if (null != jobDetail2) {
                LOGGER.info("重启后jobDetail2：" + jobDetail2.toString());
            }else {
                LOGGER.info("重启后jobDetail2为空");
            }
        } catch (Exception e) {
            throw new BusinessException("恢复任务失败:" + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(JobEntity jobEntity) {
        try {
            checkJobEntity(jobEntity);
            Assert.hasLength(jobEntity.getId(), "jobEntity id 为空");
            jobEntityService.removeById(jobEntity);

            TriggerKey triggerKey = TriggerKey.triggerKey(jobEntity.getJobName(), jobEntity.getJobGroup());
            // 停止触发器
            scheduler.pauseTrigger(triggerKey);
            // 移除触发器
            scheduler.unscheduleJob(triggerKey);
            // 删除任务
            scheduler.deleteJob(JobKey.jobKey(jobEntity.getJobName(), jobEntity.getJobGroup()));
            LOGGER.info("removeJob:" + JobKey.jobKey(jobEntity.getJobName()));
        } catch (Exception e) {
            throw new BusinessException("移除任务失败:" + e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTask(JobEntity jobEntity) {
        /*
         * 1.更新 2.删除任务 新建任务
         */
        try {
            jobEntity.setModifyTime(new Date());
            jobEntityService.updateById(jobEntity);
            //移除任务
            TriggerKey triggerKey = TriggerKey.triggerKey(jobEntity.getJobName(), jobEntity.getJobGroup());
            // 停止触发器
            scheduler.pauseTrigger(triggerKey);
            // 移除触发器
            scheduler.unscheduleJob(triggerKey);
            // 删除任务
            scheduler.deleteJob(JobKey.jobKey(jobEntity.getJobName(), jobEntity.getJobGroup()));
            LOGGER.info("removeJob:" + JobKey.jobKey(jobEntity.getJobName()));
            //新增任务
            Class cls = Class.forName(jobEntity.getJobClassName());
            cls.newInstance();
            // 构建job信息
            JobDetail job = JobBuilder.newJob(cls).withIdentity(jobEntity.getJobName(), jobEntity.getJobGroup())
                .withDescription(jobEntity.getRemark()).build();
            // 触发时间点
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(jobEntity.getCronExpression());
            Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger_" + jobEntity.getJobName(), jobEntity.getJobGroup()).startNow()
                .withSchedule(cronScheduleBuilder).build();
            // 传递的参数
            job.getJobDataMap().put("jobtasktype", jobEntity.getJobTaskType());
            job.getJobDataMap().put("jobtype", jobEntity.getJobType());
            job.getJobDataMap().put("apiurl", jobEntity.getApiUrl());
            job.getJobDataMap().put("params", jobEntity.getParams());
            // 交由Scheduler安排触发
            if (JobStatusEnum.ON.getType().equals(jobEntity.getJobStatus())) {
                scheduler.scheduleJob(job, trigger);
            }
            // 如果是立即运行则首先触发一次任务
            if (JobStatusEnum.ON.getType().equals(jobEntity.getJobStatus()) && IsNowRunEnum.YES.getType().equals(jobEntity.getIsNowRun())) {
                JobKey key = new JobKey(jobEntity.getJobName(), jobEntity.getJobGroup());
                scheduler.triggerJob(key, job.getJobDataMap());
            }
        } catch (Exception e) {
            throw new BusinessException("更新任务失败: 原因是："+ e.getMessage());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdateTaskSetStartTime(JobEntity jobEntity) {
        /*
         * 1.将任务记录插入 linkapp_job_entity
         * 2.将任务交由Scheduler安排触发 3.如果是立即执行，则首先触发一次任务
         */
        try {
            jobEntity.setTriggerName("trigger_" + jobEntity.getJobName());
            jobEntity.setTriggerGroup("trigger_" + jobEntity.getJobGroup());
            jobEntityService.saveOrUpdate(jobEntity);
        } catch (Exception e) {
            throw new BusinessException("定时任务插入数据库失败: "+ e.getMessage());
        }
        try {
            Class cls = Class.forName(jobEntity.getJobClassName());
            cls.newInstance();
            // 构建job信息
            JobDetail job = JobBuilder.newJob(cls).withIdentity(jobEntity.getJobName(), jobEntity.getJobGroup())
                    .withDescription(jobEntity.getRemark()).build();
            // 触发时间点
            CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(jobEntity.getCronExpression())
                    .withMisfireHandlingInstructionDoNothing();//不触发立即执行等待下次Cron触发频率到达时刻开始按照Cron频率依次执行
            Trigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity("trigger_" + jobEntity.getJobName(), jobEntity.getJobGroup())
                    .startAt(jobEntity.getStartDate())
                    .endAt(jobEntity.getEndDate())
                    .withSchedule(cronScheduleBuilder).build();
            // 传递的参数
            job.getJobDataMap().put("jobtasktype", jobEntity.getJobTaskType());
            job.getJobDataMap().put("jobtype", jobEntity.getJobType());
            job.getJobDataMap().put("apiurl", jobEntity.getApiUrl());
            job.getJobDataMap().put("params", jobEntity.getParams());
            JobKey key = new JobKey(jobEntity.getJobName(), jobEntity.getJobGroup());
            //先清除原有定时任务在添加
            scheduler.pauseTrigger(trigger.getKey());
            scheduler.unscheduleJob(trigger.getKey());
            scheduler.deleteJob(key);
            // Thread.sleep(1L*1000L);
            // 交由Scheduler安排触发
            if (JobStatusEnum.ON.getType().equals(jobEntity.getJobStatus())) {
                scheduler.scheduleJob(job, trigger);
            }
            // 如果是立即运行则首先触发一次任务
            if (JobStatusEnum.ON.getType().equals(jobEntity.getJobStatus()) && IsNowRunEnum.YES.getType().equals(jobEntity.getIsNowRun())) {
                scheduler.triggerJob(key, job.getJobDataMap());
            }
        } catch (Exception e) {
            if (e instanceof ObjectAlreadyExistsException) {
                LOGGER.error(e.getMessage(), e);
                throw new BusinessException("任务名称存在重复，请换个任务名重试");
            }
            throw new BusinessException("新增任务失败,错误信息如下:"+ e.getMessage());
        }
    }

}
